/*    
 * Copyright (C) 2006  ankostis, Stefan Kuper

 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2.1 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

package net.googlecode.weblogic_jaxb1codec;

import java.util.ArrayList;
import java.util.Iterator;

import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;

import weblogic.xml.stream.ElementFactory;
import weblogic.xml.stream.XMLOutputStream;
import weblogic.xml.stream.XMLStreamException;

/**
 * @author SK, ankostis
 */
class SerializerContentHandler implements ContentHandler {
    
    private class PrefixMapping {
        public String prefix;
        public String uri;
        
        public PrefixMapping(String prefix, String uri) {
            this.prefix = prefix;
            this.uri = uri;
        }
    }

    private XMLOutputStream outputStream;

    /**
     * Contains the latest prefixMappings that have not been used yet,
     * and they will be cleared after the next element includes them.
     * Created lazily.
     */
    private ArrayList prefixMappings;
    
	/**
	 * @param outputStream The {@link XMLOutputStream}&nbsp;to write to.
	 */
	public SerializerContentHandler(XMLOutputStream outputStream) {
		this.outputStream = outputStream;
	}

    /**
     * WARN: Do not invoke when prefixMappings == null
     * @throws XMLStreamException 
     */
    private void addPrefixMappingAttributes() throws XMLStreamException {
        Iterator it = prefixMappings.iterator();
        while(it.hasNext()) {
            PrefixMapping pm = (PrefixMapping)it.next();
            outputStream.add(ElementFactory.createNamespaceAttribute(
                    pm.prefix, pm.uri));
//            System.out.print(" xmlns:" + pm.prefix+"='"+pm.uri+"'");
        }
        
        prefixMappings.clear();
    }
    
    /**
     * @inheritDoc
     */
    public void startPrefixMapping(final String prefix, final String uri) {
        if (prefixMappings == null) // Lazily creates it.
            prefixMappings = new ArrayList();
        
        prefixMappings.add(new PrefixMapping(prefix, uri));
    }

    /**
     * @inheritDoc
     */
    public void endPrefixMapping(final String prefix) {
    }

	/**
	 * @inheritDoc
	 */
	public void startElement(String uri, String localName, String qName, Attributes saxAttrs) throws SAXException {
		try {
            outputStream.add(ElementFactory.createStartElement(uri, localName));
//            System.out.print("<{"+uri+"}"+qName);
//            System.out.print("<"+qName);

            // Add any namespaces.
            if (this.prefixMappings != null && !this.prefixMappings.isEmpty())
                addPrefixMappingAttributes();
            
            // Add any atributes.
            for(int i = 0; i < saxAttrs.getLength(); i++) {
                outputStream.add(ElementFactory.createAttribute(
                        saxAttrs.getURI(i), saxAttrs.getLocalName(i), saxAttrs.getValue(i)));
//            System.out.print(" " + saxAttrs.getQName(i)+"='"+saxAttrs.getValue(i)+"'");
            }
//            System.out.print(">");
        } catch (XMLStreamException ex) {
            throw new SAXException("Error while starting element '" + localName + "': " + ex.getMessage(), ex);
		}
	}

	/**
	 * @inheritDoc
	 */
	public void endElement(String uri, String localName, String qName) throws SAXException {
		try {
			outputStream.add(ElementFactory.createEndElement(uri, localName));
//            System.out.print("</"+qName+">\n");
        } catch (XMLStreamException ex) {
            throw new SAXException("Error while ending element '" + localName + "': " + ex.getMessage(), ex);
        }
	}

	/**
	 * @inheritDoc
	 */
	public void characters(char[] ch, int start, int length) throws SAXException {
		try {
			outputStream.add(ElementFactory.createCharacterData(String.copyValueOf(ch, start, length)));
//            System.out.print(String.copyValueOf(ch, start, length));
        } catch (XMLStreamException ex) {
            throw new SAXException("Error while adding characters: " + ex.getMessage(), ex);
		}
	}

    /**
     * @throws SAXException 
     * @inheritDoc
     */
    public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException{
        try {
            outputStream.add(ElementFactory.createSpace(String.copyValueOf(ch, start, length)));
//            System.out.print(String.copyValueOf(ch, start, length));
        } catch (XMLStreamException ex) {
            throw new SAXException("Error while adding ignoreable whitespace: " + ex.getMessage(), ex);
        }
    }

    /**
     * @throws SAXException 
     * @inheritDoc
     */
    public void processingInstruction(String target, String data) throws SAXException{
        //XXX: Untested code.
        try {
            outputStream.add(
                    ElementFactory.createProcessingInstruction(ElementFactory.createXMLName(target), 
                            data));
//            System.out.print(target+"='"+data+"'");
        } catch (XMLStreamException ex) {
            throw new SAXException("Error while creating proc instruction: " + ex.getMessage(), ex);
        }
    }

    /**
     * @inheritDoc
     */
    public void setDocumentLocator(Locator locator) {
    }

    /**
     * @inheritDoc
     */
    public void skippedEntity(String name){
    }

    /**
     * @inheritDoc
     */
    public void startDocument(){
//        System.out.println("PRINTING DOC:");
    }

    /**
     * @throws XMLStreamException 
     * @inheritDoc
     */
    public void endDocument() throws SAXException{
        try {
            outputStream.flush();
        } catch (XMLStreamException ex) {
            throw new SAXException("Error while ending document: " + ex.getMessage(), ex);
        }
    }
}
